/////////////////////////////////////////////////////
// File Name: queue_manager_v1.v
// Author: zeping fan
// mail:   zpfan007@163.com
// Created Time: 2023年06月27日 星期二 23时30分31秒
/////////////////////////////////////////////////////

module  queue_manager#(
    parameter   PORT_NUM = 4
)
(
//system interface
clk,
rst_n,
//port_id,

//frame_process interface
sof,
dat_vld,
din,
bp,

//queue interface 
ptr_fifo_rd,
ptr_fifo_dout,
ptr_fifo_empty,
data_fifo_rd,
data_fifo_dout
);


input                           clk;
input                           rst_n;

input                           sof;
input                           dat_vld;
input   [7:0]                   din;
output                          bp;

input   [PORT_NUM-1:0]          ptr_fifo_rd;
output  [PORT_NUM-1:0][15:0]    ptr_fifo_dout;
output  [PORT_NUM-1:0]          ptr_fifo_empty;
input   [PORT_NUM-1:0]          data_fifo_rd;
output  [PORT_NUM-1:0][7:0]     data_fifo_dout;
 
wire                            bp;
wire    [PORT_NUM-1:0]          port_bp;
wire    [PORT_NUM-1:0][15:0]    ptr_fifo_dout;
wire    [PORT_NUM-1:0]          ptr_fifo_empty;
wire    [PORT_NUM-1:0][7:0]     data_fifo_dout;

reg     [PORT_NUM-1:0]          ptr_fifo_wr;
reg     [PORT_NUM-1:0][15:0]    ptr_fifo_din;
wire    [PORT_NUM-1:0]          ptr_fifo_full;

reg     [PORT_NUM-1:0]          data_fifo_wr;
reg     [PORT_NUM-1:0][7:0]     data_fifo_din;
wire    [PORT_NUM-1:0][12:0]    data_fifo_wr_cnt;
//FSM state
reg     [2:0]                   cur_state;
reg     [2:0]                   nxt_state;

//demux sel缓存
reg     [PORT_NUM-1:0]          sel_r;
wire    [PORT_NUM-1:0]          sel;

//demux数据输出
wire    [PORT_NUM-1:0][7:0]     demux_data;

//数据包长度
reg     [10:0]                  length;

localparam  IDLE    =   3'b001;
localparam  HEADER  =   3'b010;
localparam  TRANS   =   3'b100;

//============================FSM===================================
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        cur_state[2:0]  <=  IDLE;
    else 
        cur_state[2:0]  <=  nxt_state[2:0];
end

always @(*)begin
    case(cur_state[2:0])
        IDLE:   nxt_state[2:0] = (sof && !bp)? HEADER : IDLE;
        HEADER: nxt_state[2:0] = TRANS;
        TRANS:  nxt_state[2:0] = dat_vld? TRANS : IDLE;
        default:nxt_state[2:0] = IDLE;
    endcase
end

//=========================input register===========================
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        sel_r[PORT_NUM-1:0] <= {PORT_NUM{1'b0}};
    else if(sof)    
        sel_r[PORT_NUM-1:0] <= din[PORT_NUM-1:0];
end

assign sel[PORT_NUM-1:0] = sof? din[PORT_NUM-1:0] : sel_r[PORT_NUM-1:0];

//============================length=================================
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        length[10:0] <= 11'b0;
    else if(sof)
        length[10:8] <= din[6:4];
    else if(cur_state[2:0]==HEADER)
        length[7:0]  <= din[7:0];
end

//==========================fifo write===============================
assign  bp = |(port_bp[PORT_NUM-1:0]);

generate
genvar  i;
for(i=0;i<PORT_NUM;i=i+1)begin:fifo_wr_loop

    assign port_bp[i] = ptr_fifo_full[i] | (data_fifo_wr_cnt[i][12:0]>2578);

    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            data_fifo_wr[i]         <= 1'b0;
            data_fifo_din[i][7:0]   <= 8'b0;
        end               
        else if(!dat_vld)begin
            data_fifo_wr[i]         <= 1'b0;
            data_fifo_din[i][7:0]   <= 8'b0;
        end   
        else if(cur_state[2:0]==TRANS && sel[i])begin
            data_fifo_wr[i]         <= 1'b1;
            data_fifo_din[i][7:0]   <= demux_data[i][7:0];
        end
    end

    always @(posedge clk or negedge rst_n)begin
        if(!rst_n)begin
            ptr_fifo_wr[i]          <= 1'b0;
            ptr_fifo_din[i][15:0]   <= 16'b0;
        end
        else if(cur_state[2:0]==TRANS && nxt_state[2:0]==IDLE && sel[i])begin
            ptr_fifo_wr[i]          <= 1'b1;
            ptr_fifo_din[i][15:0]   <= {4'b0,length[10:0]}-11'd2;
        end
        else begin
            ptr_fifo_wr[i]          <= 1'b0;
            ptr_fifo_din[i][15:0]   <= 16'b0;
        end
    end

end
endgenerate


//==========================demux===================================
one_hot_demux_2d#(
    .WIDTH(8),
    .CNT(4)
)
x_data_demux(
    .din(din),
    .sel(sel),
    .dout(demux_data[PORT_NUM-1:0])
);

//==========================queue fifo==============================
genvar j;
generate for(j=0;j<PORT_NUM;j=j+1)begin:sfifo_loop
sfifo#(
    .DEPTH(32),
    .WIDTH(16)
)
x_ptr_fifo(
    .clk(clk),
    .data_in(ptr_fifo_din[j]),
    .data_out(ptr_fifo_dout[j]),
    .empty_n(), 
    .empty(ptr_fifo_empty[j]),
    .full_n(),  
    .full(ptr_fifo_full[j]),
    .rd_en(ptr_fifo_rd[j]),
    .rst_n(rst_n),
    .wr_en(ptr_fifo_wr[j]),
    .almost_full_n(),
    .almost_empty_n(),
    .cnt()
);

sfifo#(
    .DEPTH(4096),
    .WIDTH(8)    
)
x_data_fifo(
    .clk(clk),
    .data_in(data_fifo_din[j]),
    .data_out(data_fifo_dout[j]),
    .empty_n(), 
    .empty(),
    .full_n(),  
    .full(),
    .rd_en(data_fifo_rd[j]),
    .rst_n(rst_n),
    .wr_en(data_fifo_wr[j]),
    .almost_full_n(),
    .almost_empty_n(),
    .cnt(data_fifo_wr_cnt[j])
);

end
endgenerate
    

endmodule
